/*
    VR-TOOLBOX
    Copyright (C) 2013 Nicolas GUILLIER 

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/
package com.ngu.headingcalc;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.View;

public class PolarPlotView extends View {

	WindPolar polar;
	float scale;

	int margin = 50;
	int primaryUnit = 10;
	int secondaryUnit = 5;

	// Polar boat speeds
	Path polarLine;
	Paint polarLineStyle;
	List<Pt> maxSpeedPoints;
	Paint maxSpeedPointsStyle;

	// polar VMC speed
	Path vmcLine;
	Paint vmcLineStyle;
	List<Pt> maxSpeedVmcPoints;

	// Scale
	// Secondary unit
	Path secondaryUnitLine;
	Paint secondaryUnitStyle;

	// Primary unit
	Path primaryUnitLine;
	Paint primaryUnitStyle;

	// Controled Values
	int windDir;
	float windSpeed;
	int targetDir;

	boolean computationDone = false;

	public PolarPlotView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init();
	}

	public PolarPlotView(Context context) {
		super(context);
		init();
	}

	public PolarPlotView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	protected void init() {
		// Log.d("HeadCalc", "init");

		polarLineStyle = new Paint();
		polarLineStyle.setStyle(Paint.Style.STROKE);
		polarLineStyle.setColor(Color.YELLOW);
		polarLineStyle.setStrokeWidth(4);

		vmcLineStyle = new Paint();
		vmcLineStyle.setStyle(Paint.Style.STROKE);
		vmcLineStyle.setColor(Color.MAGENTA);
		vmcLineStyle.setStrokeWidth(2);

		maxSpeedPointsStyle = new Paint();
		maxSpeedPointsStyle.setStyle(Paint.Style.STROKE);
		maxSpeedPointsStyle.setColor(Color.RED);
		maxSpeedPointsStyle.setStrokeWidth(10);

		primaryUnitStyle = new Paint();
		primaryUnitStyle.setStyle(Paint.Style.STROKE);
		primaryUnitStyle.setColor(Color.CYAN);
		primaryUnitStyle.setStrokeWidth(2);
		primaryUnitStyle.setAlpha(120);

		secondaryUnitStyle = new Paint();
		secondaryUnitStyle.setStyle(Paint.Style.STROKE);
		secondaryUnitStyle.setColor(Color.CYAN);
		secondaryUnitStyle.setStrokeWidth(1);
		secondaryUnitStyle.setPathEffect(new DashPathEffect(
				new float[] { 8, 7 }, 5));
		secondaryUnitStyle.setAlpha(130);

		polarLine = null;
		vmcLine = null;
		primaryUnitLine = null;
		secondaryUnitLine = null;

		polar = null;
		maxSpeedPoints = null;
		maxSpeedVmcPoints = null;

		windDir = 0;
		windSpeed = 0f;
		targetDir = 0;

		computationDone = false;
	}

	public void setWindPolar(WindPolar polar) {
		this.polar = polar;
		computationDone = false;
	}

	public void setWind(int windDir, float windSpeed, int targetDir) {
		this.windDir = windDir;
		this.windSpeed = windSpeed;
		this.targetDir = targetDir;

		computationDone = false;
		invalidate();
	}

	public void computePolars() {
		Pt[] polarValues = new Pt[360];
		Pt[] vmcValues = new Pt[360];
		float maxSpeed = -1;
		float maxVmc = -1;
		for (int d = 0; d < 360; d++) {

			// Compute true speed
			int twa = MainActivity.getAngleDiff(d, windDir);
			float speed = polar.getBoatSpeed(twa, windSpeed);
			polarValues[d] = new Pt(d, speed);
			if (maxSpeed < speed) {
				maxSpeed = speed;
			}

			// Compute Velocity Made to Course (=to target)
			int vmcAngle = MainActivity.getAngleDiff(d, targetDir);
			speed = speed * FloatMath.cos((float) Math.toRadians(vmcAngle));
			if (speed < 0f) {
				speed = 0f;
			}
			vmcValues[d] = new Pt(d, speed);

			if (maxVmc < speed) {
				maxVmc = speed;
			}
		}

		scale = (getMeasuredHeight() / 2 - margin) / maxSpeed;

		if (polarLine == null) {
			polarLine = new Path();
		} else {
			polarLine.reset();
		}
		maxSpeedPoints = new ArrayList<Pt>();

		// Plot first point in path
		Pt rectPoint = coord(polarValues[0].x, polarValues[0].y);
		polarLine.moveTo(rectPoint.x, rectPoint.y);

		// Plot all points
		for (Pt polarPoint : polarValues) {
			rectPoint = coord(polarPoint.x, polarPoint.y);
			polarLine.lineTo(rectPoint.x, rectPoint.y);

			// select the maxima
			if (polarPoint.y >= maxSpeed) {
				maxSpeedPoints.add(rectPoint);
			}
		}

		if (vmcLine == null) {
			vmcLine = new Path();
		} else {
			vmcLine.reset();
		}
		maxSpeedVmcPoints = new ArrayList<Pt>();

		// Plot first point in path
		rectPoint = coord(vmcValues[0].x, vmcValues[0].y);
		vmcLine.moveTo(rectPoint.x, rectPoint.y);

		// Plot all points
		for (Pt polarPoint : vmcValues) {
			rectPoint = coord(polarPoint.x, polarPoint.y);
			if (polarPoint.y > 0f) {
				vmcLine.lineTo(rectPoint.x, rectPoint.y);
				// Log.d("HeadCalc","angle=" + polarPoint.x + ", speed=" +
				// polarPoint.y + " goes to:" + rectPoint.toString());

				// select the maxima
				if (polarPoint.y >= maxVmc) {
					maxSpeedVmcPoints.add(rectPoint);
				}
			}
		}

		// Scale lines
		// Primary Units
		int unit = secondaryUnit;
		primaryUnitLine = new Path();
		secondaryUnitLine = new Path();
		while (unit <= maxSpeed) {
			if (unit % primaryUnit == 0) {
				primaryUnitLine.addCircle(getMeasuredWidth() / 2,
						getMeasuredHeight() / 2, unit * scale,
						Path.Direction.CW);
			} else if (unit % secondaryUnit == 0) {
				secondaryUnitLine.addCircle(getMeasuredWidth() / 2,
						getMeasuredHeight() / 2, unit * scale,
						Path.Direction.CW);
			}
			unit++;
		}

		computationDone = true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.view.View#onDraw(android.graphics.Canvas)
	 */
	@Override
	protected void onDraw(Canvas canvas) {
		if (!computationDone) {
			computePolars();
		}

		// Draw Speed scale
		// Primary Unit Circle
		if (primaryUnitLine != null) {
			canvas.drawPath(primaryUnitLine, primaryUnitStyle);
		}

		// Secondary Unit Circle
		if (secondaryUnitLine != null) {
			canvas.drawPath(secondaryUnitLine, secondaryUnitStyle);
		}

		// Draw polar
		if (polarLine != null) {
			canvas.drawPath(polarLine, polarLineStyle);
		}

		// Draw max speeds points
		if (maxSpeedPoints != null) {
			for (Pt maxSpeedPoint : maxSpeedPoints) {
				canvas.drawPoint(maxSpeedPoint.x, maxSpeedPoint.y,
						maxSpeedPointsStyle);
			}
		}

		// Draw VMC polar
		if (vmcLine != null) {
			// Log.d("HeadCalc","vmcLine not null !!");
			canvas.drawPath(vmcLine, vmcLineStyle);
			// Log.d("HeadCalc", ""+vmcLine.isEmpty());
		}

		// Draw max VMC points
		if (maxSpeedVmcPoints != null) {
			for (Pt maxSpeedPoint : maxSpeedVmcPoints) {
				canvas.drawPoint(maxSpeedPoint.x, maxSpeedPoint.y,
						maxSpeedPointsStyle);
			}
		}
	}

	private Pt coord(float angle, float speed) {
		float x = (getMeasuredWidth() / 2)
				+ FloatMath.sin((float) Math.toRadians(angle)) * speed * scale;
		float y = (getMeasuredHeight() / 2)
				- FloatMath.cos((float) Math.toRadians(angle)) * speed * scale;
		return new Pt(x, y);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.view.View#onMeasure(int, int)
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int h = 1000;
		int w = 1000;

		switch (MeasureSpec.getMode(heightMeasureSpec)) {
		case MeasureSpec.AT_MOST:
			int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
			if (maxHeight < h) {
				h = maxHeight;
			}
			break;
		case MeasureSpec.EXACTLY:
			h = MeasureSpec.getSize(heightMeasureSpec);
		}

		switch (MeasureSpec.getMode(widthMeasureSpec)) {
		case MeasureSpec.AT_MOST:
			int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
			if (maxWidth < w) {
				w = maxWidth;
			}
			break;
		case MeasureSpec.EXACTLY:
			w = MeasureSpec.getSize(widthMeasureSpec);
		}

		setMeasuredDimension(w, h);
		//Log.d("HeadCalc", "PolarPlotView: MeasuredWidth=" + getMeasuredWidth()
		//		+ ", MeasuredHeight=" + getMeasuredHeight());
	}

	private class Pt {
		float x, y;

		Pt(float _x, float _y) {
			x = _x;
			y = _y;
		}

		public String toString() {
			return "" + x + ',' + y;
		}
	}
}
